// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of DOM3XPath
//
// DOM3XPath is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// DOM3XPath is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with DOM3XPath. If not, see <http://www.gnu.org/licenses/>.
//	
//
//   $Id$
// XPathResult.cpp : Implementation of CXPathResult
#include "stdafx.h"
#include <DOM3XPath.h>
#include "XPathResult.h"

/////////////////////////////////////////////////////////////////////////////
// CXPathResult

STDMETHODIMP CXPathResult::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] = 
	{
		&IID_IXPathResult
	};
	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (::InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}

STDMETHODIMP CXPathResult::get_resultType(short *pVal)
{
	*pVal = this->m_uResultType;
	return S_OK;
}

STDMETHODIMP CXPathResult::get_numberValue(double *pVal)
{
	if(NUMBER_TYPE == m_uResultType)
	{
		*pVal =  m_dNumberValue;
		return S_OK;
	}
	else
	{
		return E_FAIL; //TYPE_ERR
	}

}

STDMETHODIMP CXPathResult::get_stringValue(BSTR *pVal)
{
	if(STRING_TYPE == m_uResultType)
	{
		m_sStringValue.CopyTo(pVal);
		return S_OK;
	}
	else
	{
		return E_FAIL; //TYPE_ERR
	}
}



STDMETHODIMP CXPathResult::get_booleanValue(BOOL *pVal)
{
	if(BOOLEAN_TYPE == m_uResultType)
	{
		*pVal = m_bBooleanValue;
		return S_OK;
	}
	else
	{
		return E_FAIL; //TYPE_ERR
	}
}

STDMETHODIMP CXPathResult::get_invalidIteratorState(BOOL *pVal)
{
	*pVal = m_bInvalidIteratorState;
	return S_OK; 
}

STDMETHODIMP CXPathResult::get_snapshotLength(unsigned long *pVal)
{
	
	*pVal =  m_ulSnapshotLength;
	return S_OK;
}

STDMETHODIMP CXPathResult::get_singleNodeValue(IXMLDOMNode **pVal)
{
	if(m_uResultType!=FIRST_ORDERED_NODE_TYPE)
	{
		return E_FAIL;//TYPE_ERR
	}
	
	m_pSingleNodeValue.CopyTo(pVal);

	return S_OK;
}

STDMETHODIMP CXPathResult::iterateNext(IXMLDOMNode **retVal)
{

	if(m_uResultType!=UNORDERED_NODE_ITERATOR_TYPE 
		&& m_uResultType!=ORDERED_NODE_ITERATOR_TYPE)
	{
		return E_FAIL;//TYPE_ERR
	}
	else if(m_pNodeList)
	{
		m_pNodeList->nextNode(retVal);
		return S_OK;
	}
	return S_FALSE;
}

STDMETHODIMP CXPathResult::snapshotItem(unsigned long lIndex,IXMLDOMNode **retVal)
{
	if(false && m_uResultType!=UNORDERED_NODE_SNAPSHOT_TYPE
		&& m_uResultType!=UNORDERED_NODE_SNAPSHOT_TYPE)
	{
		return E_FAIL;//TYPE_ERR
	}
	else if(m_pNodeList)
	{
		//This should call addref for itself, and since I am just passing
		//	the request on, I should not be interested in the refcount
		m_pNodeList->get_item(lIndex,retVal); 
		return S_OK;
	}
	return S_FALSE;

}

HRESULT CXPathResult::parseSelection(IXMLDOMNodeList *pDOMSelection)
{

	ATLTRACE("parsing Selection into XPathResult.\n");
	HRESULT hr = S_FALSE;
	LONG uLength;
	ATLTRACE("freeing old values \n");
	m_sStringValue.Empty();
	m_pNodeList.Release();
	m_pSingleNodeValue.Release();
	ATLTRACE("running\n");

	if (pDOMSelection)
	{
		hr = pDOMSelection->get_length(&uLength);
		if (SUCCEEDED(hr))
		{
			switch (this->m_uResultType)
			{
				case ORDERED_NODE_SNAPSHOT_TYPE:
				case UNORDERED_NODE_SNAPSHOT_TYPE:
				case ORDERED_NODE_ITERATOR_TYPE:
				case UNORDERED_NODE_ITERATOR_TYPE:
					m_ulSnapshotLength = uLength;
					m_pNodeList = pDOMSelection;
					break;
				case CXPathResult::EXPathResultType::ANY_TYPE:
					if(uLength != 1)
					{
						/*
							ANY_TYPE
							This code does not represent a specific type.
							An evaluation of an XPath expression will never
							produce this type. If this type is requested,
							then the evaluation returns whatever type naturally
							results from evaluation of the expression.
							
							  If the natural result is a node set when ANY_TYPE 
							was requested, then UNORDERED_NODE_ITERATOR_TYPE
							is always the resulting type. Any other representation
							of a node set must be explicitly requested.
						*/
						m_ulSnapshotLength = uLength;
						m_pNodeList = pDOMSelection;
						m_uResultType = UNORDERED_NODE_ITERATOR_TYPE;
						break;				
					}
					//If any returns one node, we continue into the
					//	default processing
				default:

					if (uLength > 0)
					{
						CComPtr<IXMLDOMNode>pNode;

						hr = pDOMSelection->get_item(0, &pNode);
						if (SUCCEEDED(hr) || m_uResultType == BOOLEAN_TYPE)
						{
							switch (m_uResultType)
							{
								case ANY_TYPE:
									if(uLength == 1) //which it will.
									{
										DOMNodeType nt;
										pNode->get_nodeType(&nt);
										switch(nt)
										{
											case NODE_ELEMENT:
											case NODE_PROCESSING_INSTRUCTION :
											case NODE_DOCUMENT_TYPE :
												m_pSingleNodeValue = pNode;
												m_uResultType = CXPathResult::EXPathResultType::FIRST_ORDERED_NODE_TYPE;
												break;
											case NODE_TEXT :
											case NODE_ATTRIBUTE:
												CComBSTR sText;
												wchar_t * pChar;
												pNode->get_text(&sText);
												double dVal = wcstod(sText,&pChar);
												if((dVal==0 && sText==pChar) || *pChar != L'\0')
												{
													//pointer returned is at the start of the given string.
													//this means that the value was not a number.
													sText.CopyTo(&m_sStringValue);
													m_uResultType = CXPathResult::EXPathResultType::STRING_TYPE;
												}
												else
												{
													m_dNumberValue = dVal;
													m_uResultType = CXPathResult::EXPathResultType::NUMBER_TYPE;
												}
		
												break;
											
										}
									}
									/*
									 * [TODO]
									 * ANY_TYPE should return whatever it can, and if the result is a node list then the
									 * result type is UNORDERED_NODE_ITERATOR_TYPE.
									 */

									break;

								case NUMBER_TYPE:
								{
									CComBSTR sText;
									wchar_t * pChar;
									pNode->get_text(&sText);
									m_dNumberValue = wcstod(sText,&pChar);
									break;
								}
								case STRING_TYPE:
									pNode->get_text(&m_sStringValue);
									break;

								case BOOLEAN_TYPE:
								{
									if(pNode)
										m_bBooleanValue = true;
									else
										m_bBooleanValue = false;
									break;
								}

								case FIRST_ORDERED_NODE_TYPE:
									m_pSingleNodeValue = pNode;
									break;
								default:
									break;
							}//switch ( on requested result type )
						}
					}
			}
		}
	}
	ATLTRACE("XPathResult.parseSelection done.\n");

	return hr;
}

#ifdef COUNT_XPATHRESULT
	unsigned long CXPathResult::sm_Count;	
#endif
